package org.windowkit.toolkit.widget;

import java.awt.AWTKeyStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;

import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JTabbedPane;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;
import javax.swing.event.ChangeListener;

/**
 * This wrapped version of a tab container enforces the use of mnemonics.
 *
 * @author Eric Lundin
 */
@SuppressWarnings("serial")
public class TabbedPane extends JComponent {

	/** The pane itself */
	protected JTabbedPane thePane;

	/**
	 * Create a TabbedPane
	 *
	 * @param componentID
	 *            The unique identifier for this component.
	 */
	public TabbedPane() {
		thePane = new JTabbedPane(SwingConstants.TOP,
				JTabbedPane.WRAP_TAB_LAYOUT);
		setLayout(new BorderLayout());
		add(thePane);
		thePane.setOpaque(true);
		setVisible(true);
	}

	/**
	 * Adds a tab to the end of the pane.
	 *
	 * @param icon
	 *            The title to display on this tab. The title to display on this
	 *            tab.
	 * @param component
	 *            The component to display when this tab is selected. The
	 *            component to display when this tab is active.
	 * @param mnemonic
	 *            The shortcut key for this tab. The shortcut key for this tab.
	 */
	public void addTab(final String title, final Component component, final char mnemonic) {
		this.addTab(title, component, mnemonic, null, null);

	}

	@Deprecated
	private void setupIcons(final int tabIndex) {
		// thePane.setDisabledIconAt(tabIndex, new TabbedPaneIconMaker(thePane
		// .getFont().getSize(), false));
		// thePane.setIconAt(tabIndex, new
		// TabbedPaneIconMaker(thePane.getFont()
		// .getSize(), true));
	}

	/**
	 * @param title
	 *            The title to display on this tab. The title to display on this
	 *            tab.
	 * @param component
	 *            The component to display when this tab is selected. The
	 *            component to display when this tab is selected.
	 * @param mnemonic
	 *            The shortcut key for this tab. The shortcut key for this tab.
	 * @param icon
	 *            The icon to display on this tab.
	 */
	public void addTab(final String title, final Component component, final char mnemonic,
			final Icon icon) {
		this.addTab(title, component, mnemonic, icon, null);
	}

	/**
	 * @param icon
	 *            The title to display on this tab. The title to display on this
	 *            tab.
	 * @param component
	 *            The component to display when this tab is selected. The
	 *            component to display when this tab is selected.
	 * @param mnemonic
	 *            The shortcut key for this tab. The shortcut key for this tab.
	 * @param toolTip
	 *            The text to display when the mouse si hovered over this tab
	 */
	public void addTab(final String title, final Component component, final char mnemonic,
			final String toolTip) {
		this.addTab(title, component, mnemonic, null, toolTip);
	}

	/**
	 * @param icon
	 *            The title to display on this tab.
	 * @param component
	 *            The component to display when this tab is selected.
	 * @param mnemonic
	 *            The shortcut key for this tab.
	 * @param icon
	 *            The icon to display on this tab.
	 * @param toolTip
	 *            The text to display when the mouse is hovered over a tab.
	 */
	public void addTab(final String title, final Component component, final char mnemonic,
			final Icon icon, final String toolTip) {

		thePane.addTab(title, icon, component, toolTip);
		int tabIndex = thePane.getTabCount() - 1;
		if (icon == null) {
			setupIcons(tabIndex);
		}
		else {
			thePane.setDisabledIconAt(tabIndex, new TabbedPaneIconMaker(
					thePane.getFont().getSize(), false));
		}

		if (mnemonic != ' ') {
			setMnemonic(tabIndex, mnemonic);
		}
	}

	/**
	 * Adds a change listener to the tabbed pane.
	 *
	 * @param listener
	 *            The ChangeListener to add.
	 */
	public void addChangeListener(final ChangeListener listener) {
		thePane.addChangeListener(listener);
	}

	/**
	 * Set a shortcut key to use to jump to this tab in combination with the
	 * mouseless key.
	 *
	 * @param tabIndex
	 *            The tab index to set the mnemonic of.
	 * @param mnemonic
	 *            The mnemonic to set for this tab.
	 */
	private void setMnemonic(final int tabIndex, final char mnemonic) {
		int keyCode = KeyStroke.getKeyStroke(mnemonic,
				java.awt.event.InputEvent.ALT_MASK).getKeyCode();
		thePane.setMnemonicAt(tabIndex, keyCode);
	}

	/**
	 * Gets the foreground for the given tab.
	 *
	 * @param index
	 *            The index of the tab.
	 * @return The color.
	 */
	public Color getForegroundAt(final int index) {
		return thePane.getForegroundAt(index);
	}

	/**
	 * Get the icon displayed on the tab at a given index.
	 *
	 * @param index
	 *            The index of the tab.
	 * @return The icon displayed on the given tab.
	 */
	public Icon getIconAt(final int index) {
		return thePane.getIconAt(index);
	}

	/**
	 * Retrieves the mnemonic assigned to a given tab.
	 *
	 * @param tabIndex
	 *            The index of the tab.
	 * @return The mnemonic assigned to this tab.
	 */
	public char getMnemonicAt(final int tabIndex) {
		return AWTKeyStroke.getAWTKeyStroke(thePane.getMnemonicAt(tabIndex), 0)
				.getKeyChar();
	}

	/**
	 * Returns a reference to the currently selected tab's component.
	 *
	 * @return The selected component.
	 */
	public Component getSelectedComponent() {
		return thePane.getSelectedComponent();
	}

	/**
	 * Returns the index of the currently selected tab.
	 *
	 * @return The index of the currently selected tab.
	 */
	public int getSelectedIndex() {
		return thePane.getSelectedIndex();
	}

	/**
	 * Returns the number of tabs in the tabbed pane.
	 *
	 * @return The number of tabs in the pane.
	 */
	public int getTabCount() {
		return thePane.getTabCount();
	}

	/**
	 * Get the component at the given tab index.
	 *
	 * @param index
	 *            The index of the tab to get the component for.
	 * @return The component at the tab.
	 */
	public Component getTabAt(final int index) {
		return thePane.getComponent(index);
	}

	/**
	 * Returns the title of the tab at the given index.
	 *
	 * @param index
	 *            The index of the tab to retrieve the title for.
	 * @return THe title of the tab at the given index.
	 */
	public String getTitleAt(final int index) {
		return thePane.getTitleAt(index);
	}

	/**
	 * Retrieves the index of the tab at the coordintes (x,y).
	 *
	 * @param x
	 *            The x coordinate.
	 * @param y
	 *            The y coordinate.
	 * @return The index at this coordinate.
	 */
	public int indexAtLocation(final int x, final int y) {
		return thePane.indexAtLocation(x, y);
	}

	/**
	 * Returns the index of the tab where the given component lives.
	 *
	 * @param component
	 *            The componenet to retrieve the index of.
	 * @return The index of the given component.
	 */
	public int indexOfComponent(final Component component) {
		return thePane.indexOfComponent(component);
	}

	/**
	 * Returns the index of the tab with the given title.
	 *
	 * @param title
	 *            The title of the requested tab.
	 * @return The index of the requested tab.
	 */
	public int indexOfTab(final String title) {
		return thePane.indexOfTab(title);
	}

	/**
	 * Returns whether the given tab is enabled.
	 *
	 * @param index
	 *            The index of the requested tab .
	 * @return Whether this tab is enabled or not.
	 */
	public boolean isEnabledAt(final int index) {
		return thePane.isEnabledAt(index);
	}

	@Override
	public void removeAll() {
		thePane.removeAll();
	}

	/**
	 * Removes the given change listener.
	 *
	 * @param l
	 *            The listener to remove.
	 */
	public void removeChangeListener(final ChangeListener l) {
		thePane.removeChangeListener(l);
	}

	/**
	 * Removes the tab and its component at the given index.
	 *
	 * @param index
	 *            The index of the tab to remove.
	 */
	public void removeTabAt(final int index) {
		thePane.removeTabAt(index);
	}

	/**
	 * Sets the component which will be displayed when the tab at the given
	 * index is selected.
	 *
	 * @param index
	 *            The index of the tabe to set the component.
	 * @param component
	 *            The component to display on the given tab.
	 */
	public void setComponentAt(final int index, final Component component) {
		thePane.setComponentAt(index, component);
	}

	/**
	 * Sets whether the given tab is enabled.
	 *
	 * @param index
	 *            The tab to enable/disable.
	 * @param enabled
	 *            True for enabled, false otherwise.
	 */
	public void setEnabledAt(final int index, final boolean enabled) {
		thePane.setEnabledAt(index, enabled);
	}

	/**
	 * Sets which component will be currently selected.
	 *
	 * @param c
	 *            The component to select.
	 */
	public void setSelectedComponent(final Component c) {
		thePane.setSelectedComponent(c);
	}

	/**
	 * Sets the currently selected tab to be the tab at the given index.
	 *
	 * @param index
	 *            The index of the tab to select.
	 */
	public void setSelectedIndex(final int index) {
		thePane.setSelectedIndex(index);
	}

	/**
	 * Draws the default icons for the tabs. Red circle for disabled. Green
	 * circle for enabled.
	 *
	 * @author Eric Lundin
	 */
	private class TabbedPaneIconMaker implements Icon {
		// The size of the icon
		int size;

		// Determines how much to scale the icon by
		static final double mult = 1.5;

		// Determines what color circle to draw
		boolean enabled;

		/**
		 * @param size
		 *            The size of the icon. Most often
		 *            component.getFont().getSize()
		 * @param enabled
		 *            Whether this tab will be enabled. If true the tab this
		 *            icon will be displayed on is enabled and we will draw a
		 *            green circle. Otherwise the tab is not enabled and we will
		 *            draw a red circle.
		 */
		public TabbedPaneIconMaker(final int size, final boolean enabled) {
			this.size = (int) (size / mult);
			this.enabled = enabled;
		}

		/**
		 * Paints an icon showing whether the current tab is enabled(green) or
		 * not (red)
		 */
		@SuppressWarnings("unused")
		public void paintIcon(final Component c, final Graphics g, final int x, final int y) {
			if (!enabled) {
				g.setColor(Color.red);
				g.fillOval(x, y, size, size);
				g.setColor(Color.BLACK);
				g.drawOval(x, y, (size - 1), (size - 1));
			} else {
				g.setColor(Color.GREEN);
				g.fillOval(x, y, size, size);
				g.setColor(Color.BLACK);
				g.drawOval(x, y, (size - 1), (size - 1));
			}
		}

		/**
		 * Returns the size of the icon.
		 */
		public int getIconWidth() {
			return (int) (size * mult);
		}

		/**
		 * Returns the size of the icon.
		 */
		public int getIconHeight() {
			return (int) (size * mult);
		}
	}

}